package com.thh.sysnlock.redis;

import com.thh.sysnlock.util.RedisPool;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class RedisDistributeLock implements Lock {


	private final Long RELEASE_SUCCESS = 1L;
	//获取锁时，睡眠等待5毫秒中
	private long SLEEP_PER = 5;
	private final String key = "lock.key";
	private final String value = "lock.value";
	private final int expireTime = 5 * 1000;
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    //setnx 版本
	private boolean tryGetDistributeLock2(Jedis jedis,String key ,String value) {
		String result = jedis.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
	}

	//lua版本
	private boolean tryGetDistributeLock(Jedis jedis,String key ,String value) {
		try {
			String luescript = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then  return redis.call('expire',KEYS[1],ARGV[2])  else return 0 end";
			List<String> keys = new ArrayList<>();
			keys.add(key);
			List<String> argv = new ArrayList<>();
			argv.add(value);
			argv.add("5");
			Object result = jedis.eval(luescript,keys,argv);
			if (RELEASE_SUCCESS.equals(result)) {
				return true;
			}
			return false;
		}catch (Exception ex) {
			ex.printStackTrace();
			return false;
		}
	}

	public boolean tryLock() {
		try(Jedis jedis = RedisPool.getJedis();){
			return tryGetDistributeLock(jedis, key, value);
		}
	}

	public void lock() {
		try(Jedis jedis = RedisPool.getJedis();){
			 while(!tryGetDistributeLock(jedis, key, value)) {
				try {
					Thread.sleep(SLEEP_PER);
				} catch (Exception e) {
				}
			 }
		}
	}

	private boolean unLock(Jedis jedis,String key ,String value) {
		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(value));

        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;

	}

	public void unlock() {
		try(Jedis jedis = RedisPool.getJedis();){
			unLock(jedis, key, value);
		}
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {

	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		return false;
	}

	@Override
	public Condition newCondition() {
		return null;
	}
}
